package cn.car.service.remote;

import cn.car.biz.carAccess.cache.AccessTokenCache;
import cn.car.constants.CarAccessConstants;
import cn.car.domains.dto.*;
import cn.car.domains.entity.VehicleCommonInfoEntity;
import cn.car.domains.entity.VehicleCommonLedgerInfoEntity;
import cn.car.domains.entity.VehicleEntryExitInfoEntity;
import cn.car.domains.entity.VehicleEntryExitLedgerInfoEntity;
import cn.car.domains.enums.InoutTypeEnum;
import cn.car.domains.info.AccessTokenInfo;
import cn.car.exception.CarAccessControlException;
import cn.car.service.config.SystemConfig;
import cn.car.service.remote.process.Processor;
import cn.car.service.remote.process.ProcessorFactory;
import cn.car.utils.Base64ToImageUtil;
import cn.car.utils.CarStringUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.List;
import java.util.function.Supplier;

@Service
@Slf4j
public class TjRemoteServiceImpl implements TjRemoteService {

    private static final Supplier<String> IMAGE_LEDGER = () -> String.join(File.separator, CarAccessConstants.getPictureFilePath().get(), "ledger");


    @Resource
    private SystemConfig systemConfig;

    @Resource
    private ProcessorFactory processorFactory;

    @Resource
    private AccessTokenCache accessTokenCache;


    @Override
    public AccessTokenDto getAccessToken(String username, String password) throws IOException {

        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }

        JSONObject entries = new JSONObject();

        long requestTimestamp = System.currentTimeMillis();
        entries.put("username", username);
        entries.put("password",  SmUtil.sm3().digestHex(username + requestTimestamp + password));
        entries.put("requestTimestamp", requestTimestamp);
        entries.put("encrypt_method", 2);

        return requestAndResultHandle(entries, AccessTokenDto.class, "/api/v1/token/getAccessToken");
    }

    @Override
    public BaseResponse putRegisterVehicleInfo(VehicleEntryExitInfoEntity entryExitInfoEntity, VehicleCommonInfoEntity vehicleCommonInfoEntity) throws IOException {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }

        String localAccessToken = getLocalAccessToken();
        JSONObject entryExitInfoObj = new JSONObject();
        entryExitInfoObj.put("plateNo", entryExitInfoEntity.getVehicleNumber());
        entryExitInfoObj.put("plateColor", vehicleCommonInfoEntity.getVehiclePlateColor());
        entryExitInfoObj.put("vehicleType", entryExitInfoEntity.getEntryExitVehicleType());
        entryExitInfoObj.put("vin", vehicleCommonInfoEntity.getVin());
        entryExitInfoObj.put("engineNo", entryExitInfoEntity.getEngineNo());
        entryExitInfoObj.put("registerDate", DateUtil.format(entryExitInfoEntity.getRegisterTime(), DatePattern.PURE_DATE_PATTERN));
        entryExitInfoObj.put("vehicleBrand", entryExitInfoEntity.getVehicleBrand());
        entryExitInfoObj.put("vehicleModel", entryExitInfoEntity.getVehicleModel());
        entryExitInfoObj.put("fuelType", entryExitInfoEntity.getFuelType());
        entryExitInfoObj.put("engineType", StrUtil.isEmpty(entryExitInfoEntity.getEngineType()) ? "未知" : entryExitInfoEntity.getEngineType());
        entryExitInfoObj.put("engineManufacturer", StrUtil.isEmpty(entryExitInfoEntity.getEngineManufacturer()) ? "未知" : entryExitInfoEntity.getEngineManufacturer());
        entryExitInfoObj.put("emissionStandard", entryExitInfoEntity.getEmissionStandard());
        entryExitInfoObj.put("connectedStatus", StrUtil.isEmpty(entryExitInfoEntity.getNetworkStatus()) ? "0" : entryExitInfoEntity.getNetworkStatus());
        entryExitInfoObj.put("useCharacter", entryExitInfoEntity.getUsageNature());
        entryExitInfoObj.put("owner", vehicleCommonInfoEntity.getOwnerName());


        SM2 sm2 = SmUtil.sm2(null, getPublicKey());
        String value = sm2.encryptHex(entryExitInfoObj.toJSONString(), CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey);


        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", localAccessToken);
        entries.put("encrypt_method", "2");
        entries.put("value", value);
        entries.put("envLicenseImg", Base64ToImageUtil.imageToBase64LimitSize(entryExitInfoEntity.getAttachedListUrl()));
        entries.put("drivingLicenseImg", Base64ToImageUtil.imageToBase64LimitSize(entryExitInfoEntity.getDrivingLicenseUrl()));

        return requestAndResultHandle(entries, BaseResponse.class, "/api/v1/vehicleInfo/putRegisterVehicleInfo");
    }


    public String getLocalAccessToken() {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }
        AccessTokenInfo accessTokenBean = accessTokenCache.get(systemConfig.getCompanyCode().get());

        String accessToken = "";
        if (accessTokenBean == null) {

            try {
                AccessTokenDto accessTokenDto = getAccessToken(systemConfig.getGovUsername().get(), systemConfig.getGovPassword().get());
                Processor processor = processorFactory.getSupport("accessTokenProcessor");
                processor.process(accessTokenDto);
                accessToken = accessTokenDto.getAccessToken();
            } catch (IOException e) {
                log.error("[getLocalAccessToken] error e:" + e);
            }
        } else {
            accessToken = accessTokenBean.getAccessToken();
        }
        return accessToken;
    }

    @Override
    public BaseResponse putVehiclePassRecords(VehicleCommonInfoEntity vehicleCommonInfoEntity, VehicleCommonLedgerInfoEntity vehicleCommonLedgerInfoEntity, VehicleEntryExitInfoEntity entryExitInfoEntity, VehicleEntryExitLedgerInfoEntity ledgerInfoEntity, InoutTypeEnum passStatus) throws IOException {

        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }


        String passTime = passStatus == InoutTypeEnum.in ?
                DateUtil.format(vehicleCommonLedgerInfoEntity.getEntryTime(), DatePattern.NORM_DATETIME_PATTERN) :
                DateUtil.format(vehicleCommonLedgerInfoEntity.getExitTime(), DatePattern.NORM_DATETIME_PATTERN);


        String vehicleHeadImg;
        String vehicleBodyImg;
        String pressTextVehicleImgUrl;
        String pressTextHeadImgUrl;
        String transportGoods;
        String transportVolume;
        String entranceNo;
        String gateNo;
        String handMove;

        if (passStatus == InoutTypeEnum.in) {
            transportGoods = ledgerInfoEntity.getEntryCargoName();
            transportVolume = ledgerInfoEntity.getEntryCargoUnit();
            pressTextVehicleImgUrl = pressTextImg(ledgerInfoEntity.getEntryVehicleImgUrl(), entryExitInfoEntity.getVehicleNumber(), passTime, vehicleCommonLedgerInfoEntity.getEntryExitCode(), vehicleCommonLedgerInfoEntity.getEntryRoadCode());
            pressTextHeadImgUrl = pressTextImg(ledgerInfoEntity.getEntryHeadImgUrl(), entryExitInfoEntity.getVehicleNumber(), passTime,  vehicleCommonLedgerInfoEntity.getEntryExitCode(), vehicleCommonLedgerInfoEntity.getEntryRoadCode());
            entranceNo = vehicleCommonLedgerInfoEntity.getEntryExitCode();
            gateNo = vehicleCommonLedgerInfoEntity.getEntryRoadCode();

            if ("SYSTEM".equals(vehicleCommonLedgerInfoEntity.getEntryOperator())) {
                handMove = "0";
            } else {
                handMove = "1";
            }
        } else if (passStatus == InoutTypeEnum.out) {
            transportGoods = ledgerInfoEntity.getExitCargoName();
            transportVolume = ledgerInfoEntity.getExitCargoUnit();
            pressTextVehicleImgUrl = pressTextImg(ledgerInfoEntity.getExitVehicleImgUrl(), entryExitInfoEntity.getVehicleNumber(), passTime, vehicleCommonLedgerInfoEntity.getExitEntryExitCode(), vehicleCommonLedgerInfoEntity.getExitRoadCode());
            pressTextHeadImgUrl = pressTextImg(ledgerInfoEntity.getExitHeadImgUrl(), entryExitInfoEntity.getVehicleNumber(), passTime, vehicleCommonLedgerInfoEntity.getExitEntryExitCode(), vehicleCommonLedgerInfoEntity.getExitRoadCode());
            entranceNo = vehicleCommonLedgerInfoEntity.getExitEntryExitCode();
            gateNo = vehicleCommonLedgerInfoEntity.getExitRoadCode();

            if ("SYSTEM".equals(vehicleCommonLedgerInfoEntity.getExitOperator())) {
                handMove = "0";
            } else {
                handMove = "1";
            }
        } else {
            log.error("[RemoteService putVehiclePassRecords] entryExitType error, entryExitType:{}", passStatus);
            return null;
        }

        vehicleHeadImg = Base64ToImageUtil.imageToBase64(pressTextVehicleImgUrl);
        vehicleBodyImg = Base64ToImageUtil.imageToBase64(pressTextHeadImgUrl);
        String pass = parsePassStatus(passStatus);

        String serialNo = systemConfig.getCompanyCode().get() + gateNo + pass + DateUtil.format(DateUtil.parse(passTime, DatePattern.NORM_DATETIME_PATTERN), "yyMMddHHmmss");

        JSONObject dto = new JSONObject();
        dto.put("serialNo", serialNo);
        dto.put("entranceNo", entranceNo);
        dto.put("gateNo", gateNo);
        dto.put("passStatus", passStatus);
        dto.put("passTime", passTime);
        dto.put("plateNo", entryExitInfoEntity.getVehicleNumber());
        dto.put("plateColor", vehicleCommonInfoEntity.getVehiclePlateColor());
        dto.put("vehicleType", entryExitInfoEntity.getEntryExitVehicleType());
        dto.put("vin", vehicleCommonInfoEntity.getVin());
        dto.put("registerDate", DateUtil.format(entryExitInfoEntity.getRegisterTime(), DatePattern.PURE_DATE_PATTERN));
        dto.put("vehicleBrand", entryExitInfoEntity.getVehicleBrand());
        dto.put("vehicleModel", entryExitInfoEntity.getVehicleModel());
        dto.put("fuelType", entryExitInfoEntity.getFuelType());
        dto.put("emissionStandard", entryExitInfoEntity.getEmissionStandard());
        dto.put("connectedStatus", StrUtil.isEmpty(entryExitInfoEntity.getNetworkStatus()) ? "0" : entryExitInfoEntity.getNetworkStatus());
        dto.put("handMove", handMove);
        dto.put("useCharacter", entryExitInfoEntity.getUsageNature());
        dto.put("transportGoodType", "4");
        if (StringUtils.isNoneBlank(transportGoods) && transportVolume != null) {
            dto.put("transportGoods", transportGoods);
            dto.put("transportVolume", transportVolume);
            dto.put("transportGoodsUnit", "kg");
        }

        SM2 sm2 = SmUtil.sm2(null, getPublicKey());
        String value = sm2.encryptHex(dto.toJSONString(), CharsetUtil.CHARSET_UTF_8, KeyType.PublicKey);
        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", getLocalAccessToken());
        entries.put("encrypt_method", "2");
        entries.put("value", value);
        entries.put("vehicleHeadImg", vehicleHeadImg);
        entries.put("vehicleBodyImg", vehicleBodyImg);


        return requestAndResultHandle(entries, SyncInfoDto.class, "/api/v1/vehiclePassRecords/putVehiclePassRecords");
    }

    @Override
    public SyncInfoDto syncInfo() throws IOException {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }

        String accessToken = getLocalAccessToken();

        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", accessToken);

        return requestAndResultHandle(entries, SyncInfoDto.class, "/api/v1/sync/syncInfo");
    }

    @Override
    public VehicleBlackPassDtoList getBlackVehiclePassInfos() throws IOException {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }
        String localAccessToken = getLocalAccessToken();
        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", localAccessToken);

        return requestAndResultHandle(entries, VehicleBlackPassDtoList.class, "/api/v1/vehicleBlacklist/breakPassRules/getVehicleInfos");
    }

    @Override
    public VehicleBlackDtoList getBlackVehicleInfos() throws IOException {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }
        String localAccessToken = getLocalAccessToken();

        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", localAccessToken);


        return requestAndResultHandle(entries, VehicleBlackDtoList.class, "/api/v1/vehicleBlacklist/breakEnvRules/getVehicleInfos");
    }

    @Override
    public ControlStrategyDto getControlStrategy() throws IOException {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            return null;
        }
        String localAccessToken = getLocalAccessToken();

        JSONObject entries = new JSONObject();
        entries.put("company_code", systemConfig.getCompanyCode().get());
        entries.put("access_token", localAccessToken);


        return requestAndResultHandle(entries, ControlStrategyDto.class, "/api/v1/VehicleControlStrategy/getControlStrategy");

    }

    private <T> T requestAndResultHandle(JSONObject entries, Class<T> clazz, String url) throws IOException {
        OkHttpClient client = new OkHttpClient();
        // 创建请求体，使用 JSON 格式
        MediaType j = MediaType.get("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(entries.toJSONString(), j);

        // 构建 POST 请求
        Request request = new Request.Builder()
                .url(systemConfig.getGovEndpoint().get() + url)
                .post(body)
                .build();

        log.info("[RemoteService getBlackVehicleInfos] remote request:{}", JSONObject.toJSONString(entries));
        // 发送请求
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful() || response.body() == null) {
                throw new IOException("Unexpected code " + response);
            }
            String result = response.body().string();
            log.info("[RemoteService getBlackVehicleInfos] remote response:{}", JSONObject.toJSONString(result));
            return JSONObject.parseObject(result, clazz);
        }
    }

    private String parsePassStatus(InoutTypeEnum passStatus) {
        return passStatus == InoutTypeEnum.in ? "1" : "2";
    }

    private String pressTextImg(String inputPath, String vehicleNumber, String passTime, String entryExitCode, String roadCode) {

        try {
            final File fileInput = FileUtil.file(inputPath);
            if (null == fileInput || !fileInput.exists()) {
                log.info("[GOV pressTextImg] 上报车辆通行记录P图异常, picMiss 源文件丢失, vehicleNumber:{}, passTime:{}, fileInput:{} not exist", vehicleNumber, passTime, inputPath);
                return inputPath;
            }

            final String plateImgName = CarStringUtil.getFileNameWithoutExtension(inputPath) + "-photoshop.jpeg";
            final String fileDir = String.join(File.separator, IMAGE_LEDGER.get());
            final String outputPath = String.join(File.separator, fileDir, plateImgName);


            ImgUtil.pressText(//
                    fileInput,
                    FileUtil.file(outputPath),
                    String.format("车牌号:%s  通行时间:%s  出入口编号:%s  道闸编号:%s", vehicleNumber, passTime, entryExitCode, roadCode), Color.WHITE,
                    new Font("宋体", Font.PLAIN, 36), //字体
                    -750, //x坐标修正值。 默认在中间，偏移量相对于中间偏移
                    750, //y坐标修正值。 默认在中间，偏移量相对于中间偏移
                    0.8f//透明度：alpha 必须是范围 [0.0, 1.0] 之内（包含边界值）的一个浮点数字
            );
            return outputPath;
        } catch (Exception e) {
            log.error("[GOV pressTextImg] 上报车辆通行记录P图异常 vehicleNumber:{}, passTime:{} Error", vehicleNumber, passTime, e);
            return inputPath;
        }
    }

    private String getPublicKey() {
        //重试三次
        String publicKey = getPublicKey0();
        for (int i = 0; i < 3; i++) {
            if (StrUtil.isNotEmpty(publicKey)) {
                break;
            }
            log.warn("[getPublicKey] retry getPublicKey");
            publicKey = getPublicKey0();
        }
        return publicKey;
    }

    private String getPublicKey0() {
        if (StrUtil.isEmpty(systemConfig.getGovEndpoint().get())) {
            log.info("[getPublicKey] govEndpoint is empty");
            return null;
        }
        AccessTokenInfo accessTokenBean = accessTokenCache.get(systemConfig.getCompanyCode().get());

        String publicKey = "";
        if (accessTokenBean == null) {

            try {
                AccessTokenDto accessTokenDto = getAccessToken(systemConfig.getGovUsername().get(), systemConfig.getGovPassword().get());
                Processor processor = processorFactory.getSupport("accessTokenProcessor");
                processor.process(accessTokenDto);
                publicKey = accessTokenDto.getPublicKey();
            } catch (IOException e) {
                log.error("[getLocalAccessToken] error e:" + e);
            }
        } else {
            publicKey = accessTokenBean.getPublicKey();
        }
        log.info("[getPublicKey] publicKey:{}", publicKey);
        return publicKey;
    }
}
